package me.zhengjie.minbearwcs.serivce.mission.processor.inbound;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.entity.DTO.agv.common.PointAndPass;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.status.InboundOrderDetailStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.InboundMissionFor5Or6FloorStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.serivce.mission.locker.StorageLockCollector;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor2.Storage2FloorLocker;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderDetailDao;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.entity.DTO.agv.task.AgvActionSource;
import me.zhengjie.minbearwcs.entity.DTO.agv.task.AgvTaskSource;
import me.zhengjie.minbearwcs.entity.DTO.agv.task.actionEnum.AgvAction;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.agv.missionEnum.AgvMissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.InboundOrderDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import me.zhengjie.minbearwcs.resource.WcsResource;
import me.zhengjie.minbearwcs.serivce.agv.task.agvTask.AgvTaskDispatcher;
import me.zhengjie.minbearwcs.serivce.mission.processor.MissionProcessor;
import me.zhengjie.minbearwcs.serivce.mission.processor.MissionProcessorCommonUtil;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.InboundStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.utils.IdWorker;

import java.time.LocalDateTime;

@Slf4j
@Component
@RequiredArgsConstructor
public class Inbound5Or6FloorMissionProcessor implements MissionProcessor {

    private final InboundOrderDetailDao inboundOrderDetailDao;
    private final AgvTaskDispatcher getPutTaskDispatcher;
    private final TransactionTemplate transactionTemplate;
    private final WcsMissionDao wcsMissionDao;
    private final WcsResource wcsResource;
    private final IdWorker idWorker;
    private final InboundStorage5Or6FloorLocker inboundStorage5Or6FloorLocker;
    private final MissionProcessorCommonUtil commonUtil;
    private final SmallStorageDao smallStorageDao;
    private final Storage2FloorLocker storage2FloorLocker;


    private final Object lock = new Object();

    @Override
    public MissionType getMissionType() {
        return MissionType.INBOUND_5_OR_6_FLOOR;
    }

    @Override
    public void process(WcsMission mission) {
        InboundMissionFor5Or6FloorStatus missionStatus = (InboundMissionFor5Or6FloorStatus) mission.getEnumMissionStatus();

        switch (missionStatus) {
            case WAIT_FOR_EXCHANGE_STORAGE:
                searchExchangeStorage(mission);
                break;
            case SEARCHING_EXCHANGE_STORAGE:
                break;
            case WAIT_FOR_DISPATCH_EXCHANGE_STORAGE:
                dispatchExchangeAgvTask(mission);
                break;
            case DISPATCHING_EXCHANGE_STORAGE:
                break;
            case PROCESSING_EXCHANGE_STORAGE:
                break;
            case WAIT_FOR_CHECK_SKU:
                break;
            case CHECKING_SKU:
                break;
            case WAIT_HANDLE_EXCEPTION:
                break;
            case WAIT_FOR_ROLLBACK_PRODUCTION_LINE:
                rollbackProductionLine(mission);
                break;
            case PROCESSING_ROLLBACK_PRODUCTION_LINE:
                break;
            case WAIT_FOR_DISPATCH_REAL_STORAGE:
                dispatchRealStorageAgvTask(mission);
                break;
            case DISPATCHING_REAL_STORAGE:
                break;
            case PROCESSING_REAL_STORAGE:
                break;
            case FINISH:
                finishWcsMission(mission);
                break;
            case CANCEL:
                break;
        }


    }

    public InboundOrderDetail getOrderDetail(Integer detailId) {
        return inboundOrderDetailDao.selectById(detailId);
    }

    public boolean statusChangeSearchExchangeStorage(Integer missionId) {
        if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_EXCHANGE_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
            synchronized (lock) {
                if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_EXCHANGE_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
                    return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                        try {
                            wcsMissionDao.updateMissionStatusById(missionId, InboundMissionFor5Or6FloorStatus.SEARCHING_EXCHANGE_STORAGE.name());
                            wcsMissionDao.updateStartTime(missionId, LocalDateTime.now());
                            return true;
                        } catch (Exception e) {
                            log.error("wcs任务状态转换出错", e);
                            status.setRollbackOnly();
                        }
                        return false;
                    }));
                }
            }
        }

        return false;
    }


    private void searchExchangeStorage(WcsMission missionInfo) {
        boolean statusChangeSearchStorage = statusChangeSearchExchangeStorage(missionInfo.getId());
        if (!statusChangeSearchStorage) {
            return;
        }

        Boolean res = false;

        synchronized (StorageLockCollector.lockExchangeStorage5FOr6F) {
            res = transactionTemplate.execute(status -> {
                try {
                    InboundOrderDetail orderDetail = getOrderDetail(Integer.parseInt(missionInfo.getAssociatedDetailId()));
                    Tuple<String> tuple = inboundStorage5Or6FloorLocker.lockExchangeStorage(
                            orderDetail.getPalletCode(),
                            orderDetail.getStoragePoint()
                    );
                    if (tuple.success) {
                        inboundOrderDetailDao.updateExchangePointById(
                                Integer.parseInt(missionInfo.getAssociatedDetailId()),
                                tuple.value
                        );
                        return true;
                    }
                } catch (Exception e) {
                    log.error("wcs任务{}，5F/6F交接区储位锁定失败", missionInfo.getMissionId(), e);
                    status.setRollbackOnly();
                }
                return false;
            });
        }


        if (Boolean.TRUE.equals(res)) {
            wcsMissionDao.updateMissionStatusById(
                    missionInfo.getId()
                    , InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_EXCHANGE_STORAGE.name()
            );
        } else {
            wcsMissionDao.updateMissionStatusById(
                    missionInfo.getId()
                    , InboundMissionFor5Or6FloorStatus.WAIT_FOR_EXCHANGE_STORAGE.name()
            );
        }


    }


    public boolean statusChangeDispatchingExchange(Integer missionId) {
        if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_EXCHANGE_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
            synchronized (lock) {
                if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_EXCHANGE_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
                    return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                        try {
                            wcsMissionDao.updateMissionStatusById(missionId, InboundMissionFor5Or6FloorStatus.DISPATCHING_EXCHANGE_STORAGE.name());
                            return true;
                        } catch (Exception e) {
                            log.error("wcs任务状态转换出错", e);
                            status.setRollbackOnly();
                        }
                        return false;
                    }));
                }
            }
        }

        return false;
    }


    public void dispatchExchangeAgvTask(WcsMission missionInfo) {
        if (!statusChangeDispatchingExchange(missionInfo.getId())) return;

        InboundOrderDetail orderDetail = getOrderDetail(Integer.parseInt(missionInfo.getAssociatedDetailId()));


        wcsResource.getExecutor().execute(() -> {
            Boolean executeRes = transactionTemplate.execute(status -> {
                try {
                    String agvTaskId = "Task_Agv_" + idWorker.nextId();
                    AgvTaskSource agvTaskSource = new AgvTaskSource(agvTaskId, AgvMissionType.INBOUND_TYPE, 1, missionInfo.getMissionId(), missionInfo.getMissionType(),
                            InboundMissionFor5Or6FloorStatus.PROCESSING_EXCHANGE_STORAGE.name(), orderDetail.getPalletCode(), null, null);
                    agvTaskSource.addAction(new AgvActionSource(smallStorageDao.selectSmallStorageCodeByQrPoint(orderDetail.getStartPoint()), AgvAction.GET))
                            .addAction(new AgvActionSource(smallStorageDao.selectSmallStorageCodeByQrPoint(orderDetail.getExchangePoint()), AgvAction.PUT));


                    if (getPutTaskDispatcher.dispatch(agvTaskSource)) {
                        wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.PROCESSING_EXCHANGE_STORAGE.name());
                    } else {
                        wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_EXCHANGE_STORAGE.name());
                    }
                    return true;
                } catch (Exception e) {
                    log.error("wcs任务：{}，生成agv任务失败", missionInfo.getMissionId(), e);
                    status.setRollbackOnly();
                }
                return false;
            });

            if (Boolean.FALSE.equals(executeRes)) {
                wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_EXCHANGE_STORAGE.name());
            }

        });
    }


    public boolean statusChangeDispatchingRealStorage(Integer missionId) {
        if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_REAL_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
            synchronized (lock) {
                if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_REAL_STORAGE == wcsMissionDao.selectMissionStatusById(missionId)) {
                    return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                        try {
                            wcsMissionDao.updateMissionStatusById(missionId, InboundMissionFor5Or6FloorStatus.DISPATCHING_REAL_STORAGE.name());
                            return true;
                        } catch (Exception e) {
                            log.error("wcs任务状态转换出错", e);
                            status.setRollbackOnly();
                        }
                        return false;
                    }));
                }
            }
        }

        return false;

    }


    public void dispatchRealStorageAgvTask(WcsMission missionInfo) {
        if (!statusChangeDispatchingRealStorage(missionInfo.getId())) return;

        InboundOrderDetail orderDetail = getOrderDetail(Integer.parseInt(missionInfo.getAssociatedDetailId()));

        synchronized (StorageLockCollector.lockStorage5FOr6F) {

            Boolean executeRes = transactionTemplate.execute(status -> {
                try {
                    String selectSmallStorageByQrPoint = smallStorageDao.selectSmallStorageCodeByQrPoint(orderDetail.getStoragePoint());
                    Tuple<PointAndPass> lockAmend = inboundStorage5Or6FloorLocker.lockAmend(selectSmallStorageByQrPoint);

                    String agvTaskId = "Task_Agv_" + idWorker.nextId();
                    AgvTaskSource agvTaskSource = new AgvTaskSource(agvTaskId, AgvMissionType.INBOUND_TYPE, 1, missionInfo.getMissionId(), missionInfo.getMissionType(),
                            InboundMissionFor5Or6FloorStatus.PROCESSING_REAL_STORAGE.name(), orderDetail.getPalletCode(), null, lockAmend.value.getPassStorage());
                    agvTaskSource.addAction(new AgvActionSource(smallStorageDao.selectSmallStorageCodeByQrPoint(orderDetail.getExchangePoint()), AgvAction.GET))
                            .addAction(new AgvActionSource(lockAmend.value.getPointStorage(), AgvAction.PUT));


                    if (getPutTaskDispatcher.dispatch(agvTaskSource)) {
                        wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.PROCESSING_REAL_STORAGE.name());
                    } else {
                        wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_REAL_STORAGE.name());
                    }
                    return true;
                } catch (Exception e) {
                    log.error("wcs任务：{}，生成agv任务失败", missionInfo.getMissionId(), e);
                    status.setRollbackOnly();
                }
                return false;
            });

            if (Boolean.FALSE.equals(executeRes)) {
                wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_REAL_STORAGE.name());
            }

        }


    }


    public boolean statusChangeProcessingRollback(Integer missionId) {
        if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE == wcsMissionDao.selectMissionStatusById(missionId)) {
            synchronized (lock) {
                if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE == wcsMissionDao.selectMissionStatusById(missionId)) {
                    return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                        try {
                            wcsMissionDao.updateMissionStatusById(missionId, InboundMissionFor5Or6FloorStatus.PROCESSING_ROLLBACK_PRODUCTION_LINE.name());
                            return true;
                        } catch (Exception e) {
                            log.error("wcs任务状态转换出错", e);
                            status.setRollbackOnly();
                        }
                        return false;
                    }));
                }
            }
        }

        return false;
    }

    public void rollbackProductionLine(WcsMission missionInfo) {
        if (!statusChangeProcessingRollback(missionInfo.getId())) return;

        InboundOrderDetail orderDetail = getOrderDetail(Integer.parseInt(missionInfo.getAssociatedDetailId()));


        // 清空预定的储位信息
        Boolean clearStorage = transactionTemplate.execute(status -> {
            try {
                String storagePoint = orderDetail.getStoragePoint();
                if (storagePoint == null) return true;
                SmallStorage smallStorage = smallStorageDao.selectByQrCode(storagePoint);
                if (orderDetail.getPalletCode().equals(smallStorage.getInboundFreezePallet())) {
                    inboundStorage5Or6FloorLocker.unLockStorage(smallStorage.getSmallStorageCode());
                }
                inboundOrderDetailDao.updateStoragePointById(orderDetail.getId(), null);
                return true;
            } catch (Exception e) {
                log.error("wcs任务：{}，生成agv任务失败", missionInfo.getMissionId(), e);
                status.setRollbackOnly();
                return false;
            }
        });

        if (Boolean.FALSE.equals(clearStorage)) {
            wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE.name());
            return;
        }

        synchronized (StorageLockCollector.lockProductLineStorage2F) {

            Boolean executeRes = transactionTemplate.execute(status -> {
                try {
                    String mediumStorageProductionLine = smallStorageDao.selectMediumStorageByQrPoint(orderDetail.getStartPoint());
                    Tuple<String> tuple = storage2FloorLocker.lockStorage(
                            mediumStorageProductionLine,
                            orderDetail.getPalletCode()
                    );
                    if (!tuple.success) return false;

                    String agvTaskId = "Task_Agv_" + idWorker.nextId();
                    AgvTaskSource agvTaskSource = new AgvTaskSource(agvTaskId, AgvMissionType.INBOUND_TYPE, 1, missionInfo.getMissionId(), missionInfo.getMissionType(),
                            InboundMissionFor5Or6FloorStatus.PROCESSING_ROLLBACK_PRODUCTION_LINE.name(), orderDetail.getPalletCode(), null, null);
                    agvTaskSource.addAction(new AgvActionSource(smallStorageDao.selectSmallStorageCodeByQrPoint(orderDetail.getExchangePoint()), AgvAction.GET))
                            .addAction(new AgvActionSource(tuple.value, AgvAction.PUT));


                    if (getPutTaskDispatcher.dispatch(agvTaskSource)) {
                        wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.PROCESSING_ROLLBACK_PRODUCTION_LINE.name());
                    } else {
                        wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE.name());
                    }
                    return true;
                } catch (Exception e) {
                    log.error("wcs任务：{}，生成agv任务失败", missionInfo.getMissionId(), e);
                    status.setRollbackOnly();
                }
                return false;
            });

            if (Boolean.FALSE.equals(executeRes)) {
                wcsMissionDao.updateMissionStatusById(missionInfo.getId(), InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE.name());
            }

        }


    }


    public void finishWcsMission(WcsMission missionInfo) {
        transactionTemplate.executeWithoutResult(status -> {
            try {
                missionInfo.setFinishTime(LocalDateTime.now());
                commonUtil.transformMissionHistory(missionInfo);
                inboundOrderDetailDao.updateStatusById(
                        Integer.parseInt(missionInfo.getAssociatedDetailId()),
                        InboundOrderDetailStatus.COMPLETE
                );
            } catch (Exception e) {
                log.error("任务结束错误", e);
                status.setRollbackOnly();
            }
        });

    }

}
